using System;
using System.Data;
using System.Collections.Generic;
using System.Text;

namespace Common.Utilities.Persistence
{
    /// <summary>
    /// 
    /// </summary>
    [Serializable]
    public class FieldFilter
    {
        /// <summary></summary>
        public string FieldName;
        /// <summary></summary>
        public System.Data.SqlDbType DbType;
        /// <summary></summary>
        public FilterOperator Operator;
        /// <summary></summary>
        public string Value;

        /// <summary>
        /// 
        /// </summary>
        public enum FilterOperator
        {
            /// <summary></summary>
            Unknown = 0,
            /// <summary></summary>
            Equals = 1,
            /// <summary></summary>
            NotEqual = 2,
            /// <summary></summary>
            Greater = 3,
            /// <summary></summary>
            GreaterOrEqual = 4,
            /// <summary></summary>
            Less = 5,
            /// <summary></summary>
            LessOrEqual = 6,
            /// <summary></summary>
            Within = 7,
            /// <summary></summary>
            Contains = 8,
            /// <summary></summary>
            Intersects = 9,
            /// <summary></summary>
            Union = 10,
            /// <summary></summary>
            Complement = 11,
            /// <summary></summary>
            Add = 12,
            /// <summary></summary>
            Minus = 13,
            /// <summary></summary>
            Times = 14,
            /// <summary></summary>
            Divide = 15,
            /// <summary></summary>
            Modulus = 16,
            /// <summary></summary>
            Like = 17,
            
        }

        /// <summary>
        /// 
        /// </summary>
        public string OperatorString
        {
            get
            {
                string op = string.Empty;
                switch (this.Operator)
                {
                    case FilterOperator.Add:
                        op = "+";
                        break;
                    case FilterOperator.Complement:
                        op = "~";
                        break;
                    case FilterOperator.Contains:
                        op = "contains";
                        break;
                    case FilterOperator.Equals:
                        op = "=";
                        break;
                    case FilterOperator.Greater:
                        op = ">";
                        break;
                    case FilterOperator.GreaterOrEqual:
                        op = ">=";
                        break;
                    case FilterOperator.Intersects:
                        op = "intersects";
                        break;
                    case FilterOperator.Less:
                        op = "<";
                        break;
                    case FilterOperator.LessOrEqual:
                        op = "<=";
                        break;
                    case FilterOperator.Minus:
                        op = "-";
                        break;
                    case FilterOperator.NotEqual:
                        op = "<>";
                        break;
                    case FilterOperator.Union:
                        op = "union";
                        break;
                    case FilterOperator.Unknown:
                        op = "?";
                        break;
                    case FilterOperator.Within:
                        op = "in";
                        break;
                    case FilterOperator.Times:
                        op = "*";
                        break;
                    case FilterOperator.Divide:
                        op = "/";
                        break;
                    case FilterOperator.Modulus:
                        op = "%";
                        break;
                    case FilterOperator.Like:
                        op = "like";
                        break;
                    default:
                        break;
                }
                return op;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public bool IsQuoteType
        {
            get
            {
                bool needQuote = false;
                switch (this.DbType)
                {
                    case System.Data.SqlDbType.BigInt:
                    case System.Data.SqlDbType.Decimal:
                    case System.Data.SqlDbType.Float:
                    case System.Data.SqlDbType.Int:
                    case System.Data.SqlDbType.Money:
                    case System.Data.SqlDbType.Real:
                    case System.Data.SqlDbType.SmallInt:
                    case System.Data.SqlDbType.SmallMoney:
                    case System.Data.SqlDbType.TinyInt:
                        needQuote = false;
                        break;
                    case SqlDbType.Char:
                    case SqlDbType.VarChar:
                    case SqlDbType.NChar:
                    case SqlDbType.NVarChar:
                        needQuote = true;
                        break;
                    case SqlDbType.NText:
                    case SqlDbType.Text:
                        needQuote = true;
                        break;
                    case SqlDbType.Bit:
                        needQuote = false;
                        break;
                    case SqlDbType.DateTime:
                    case SqlDbType.SmallDateTime:
                        needQuote = true;
                        break;
                    case SqlDbType.Binary:
                    case SqlDbType.Image:
                    case SqlDbType.VarBinary:
                        needQuote = false;
                        break;
                    default:
                        break;
                }
                return needQuote;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="subjectValue"></param>
        /// <returns></returns>
        public bool Evaluate(string subjectValue)
        {
            bool passFilter = false;
            object subValue = FieldValueConverter.ToValue(this.DbType, subjectValue);
            if (subValue != null)
            {
                switch (this.Operator)
                {
                    case FilterOperator.Contains:
                        passFilter = StringUtil.Match(StringUtil.Split(subjectValue), this.Value);
                        break;
                    case FilterOperator.Equals:
                        if (subjectValue.ToLower().Trim() == this.Value.ToLower().Trim())
                            passFilter = true;
                        break;
                    case FilterOperator.Greater:
                        if (Tuple.IsBooleanDataType(this.DbType))
                        {}
                        else if (Tuple.IsDateTimeDataType(this.DbType))
                        {
                            DateTime subjDate = (DateTime)subValue;
                            DateTime objDate = (DateTime)FieldValueConverter.ToValue(this.DbType, this.Value);
                            passFilter = (subjDate > objDate);
                        }
                        else if (Tuple.IsNumberDataType(this.DbType))
                        {
                            decimal subjNumber = (decimal)subValue;
                            decimal objNumber = (decimal)FieldValueConverter.ToValue(this.DbType, this.Value);
                            passFilter = (subjNumber > objNumber);
                        }
                        else
                        {
                            if (subjectValue.ToLower().Trim().CompareTo(this.Value.ToLower().Trim()) > 0)
                                passFilter = true;
                        }
                        break;
                    case FilterOperator.GreaterOrEqual:
                        if (Tuple.IsBooleanDataType(this.DbType))
                        {}
                        else if (Tuple.IsDateTimeDataType(this.DbType))
                        {
                            DateTime subjDate = (DateTime)subValue;
                            DateTime objDate = (DateTime)FieldValueConverter.ToValue(this.DbType, this.Value);
                            passFilter = (subjDate >= objDate);
                        }
                        else if (Tuple.IsNumberDataType(this.DbType))
                        {
                            decimal subjNumber = (decimal)subValue;
                            decimal objNumber = (decimal)FieldValueConverter.ToValue(this.DbType, this.Value);
                            passFilter = (subjNumber >= objNumber);
                        }
                        else
                        {
                            if (subjectValue.ToLower().Trim().CompareTo(this.Value.ToLower().Trim()) >= 0)
                                passFilter = true;
                        }
                        break;
                    case FilterOperator.Intersects:
                        passFilter = StringUtil.Intersects(StringUtil.Split(subjectValue), StringUtil.Split(this.Value));
                        break;
                    case FilterOperator.LessOrEqual:
                        if (Tuple.IsBooleanDataType(this.DbType))
                        {

                        }
                        else if (Tuple.IsDateTimeDataType(this.DbType))
                        {
                            DateTime subjDate = (DateTime)subValue;
                            DateTime objDate = (DateTime)FieldValueConverter.ToValue(this.DbType, this.Value);
                            passFilter = (subjDate <= objDate);
                        }
                        else if (Tuple.IsNumberDataType(this.DbType))
                        {
                            decimal subjNumber = (decimal)subValue;
                            decimal objNumber = (decimal)FieldValueConverter.ToValue(this.DbType, this.Value);
                            passFilter = (subjNumber <= objNumber);
                        }
                        else
                        {
                            if (subjectValue.ToLower().Trim().CompareTo(this.Value.ToLower().Trim()) <= 0)
                                passFilter = true;
                        }
                        break;
                    case FilterOperator.Like:
                        passFilter = StringUtil.Match(subjectValue, this.Value, StringMatchType.RegexMatch);
                        break;
                    case FilterOperator.NotEqual:
                        if (Tuple.IsBooleanDataType(this.DbType))
                        {

                        }
                        else if (Tuple.IsDateTimeDataType(this.DbType))
                        {
                            DateTime subjDate = (DateTime)subValue;
                            DateTime objDate = (DateTime)FieldValueConverter.ToValue(this.DbType, this.Value);
                            passFilter = (subjDate != objDate);
                        }
                        else if (Tuple.IsNumberDataType(this.DbType))
                        {
                            decimal subjNumber = (decimal)subValue;
                            decimal objNumber = (decimal)FieldValueConverter.ToValue(this.DbType, this.Value);
                            passFilter = (subjNumber != objNumber);
                        }
                        else
                        {
                            if (subjectValue.ToLower().Trim().CompareTo(this.Value.ToLower().Trim()) != 0)
                                passFilter = true;
                        }
                        break;
                    case FilterOperator.Within:
                        passFilter = StringUtil.Contains(StringUtil.Split(this.Value), subjectValue);
                        break;
                    default:
                        break;
                }
            }
            return passFilter;
        }
    }
}
